package cloud.seckill.seckillserver.service.impl;

import cloud.seckill.seckillserver.common.constant.RedisConstant;
import cloud.seckill.seckillserver.common.constant.SystemConstant;
import cloud.seckill.seckillserver.common.redis.RedisUtil;
import cloud.seckill.seckillserver.model.result.Result;
import cloud.seckill.seckillserver.service.RedisService;
import cloud.seckill.seckillserver.service.SeckillService;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import static cloud.seckill.seckillserver.common.constant.RedisConstant.KEY_PREFIX_LOCK;

/**
 * @title: SeckillServiceImpl
 * @Author Wangsj
 * @Date: 2021/5/24
 */
@Slf4j
@Service
public class SeckillServiceImpl implements SeckillService {


    private static final String CLOSE = "商品卖完了，收摊回家！！！";

    private static final String ORDER = "抢到一件商品！！";

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    Redisson redisson;

    /**
     * @return void
     * @Author Wangsj
     * @Description 级别1秒杀，出现线程安全问题，无法秒杀
     * @Date 22:25 2021/5/24
     * @Param []
     **/
    @Override
    public Result seckillLv1() {
        Integer num = getGoodsNum();
        if (num > 0) {
            Integer afterOrder = num - 1;
            redisUtil.cacheValue(RedisConstant.GOODS_KEY, afterOrder);
            String msg = orderMessage(afterOrder);
            log.info(msg);
            return Result.ok(msg);
        } else {
            log.info(CLOSE);
            return Result.ok(CLOSE);
        }
    }

    @Override
    public Result seckillLv2() {
        synchronized (this) {
            return seckillLv1();
        }
    }

    @Override
    public Result seckillLv3() throws InterruptedException {
        String value = UUID.randomUUID().toString() + Thread.currentThread().getName();
        try {
            if (!redisUtil.setCacheLock(KEY_PREFIX_LOCK, value)) {
                return Result.err("没抢上啊！！！");
            }
            return seckillLv1();
        } finally {
            //防止删除其他线程的锁
            if (redisUtil.getCacheLock(KEY_PREFIX_LOCK).equals(value)) {
                redisUtil.removeCacheLock(KEY_PREFIX_LOCK);
            }

        }
    }

    @Override
    public Result seckillLv4() throws Exception {
        String value = UUID.randomUUID().toString() + Thread.currentThread().getName();
        try {
            if (!redisUtil.setCacheLock(KEY_PREFIX_LOCK, value)) {
                log.error("没抢上啊！！！");
                return Result.err("没抢上啊！！！");
            }
            Integer num = getGoodsNum();
            if (num > 0) {
                Integer afterOrder = num - 1;
                redisUtil.cacheValue(RedisConstant.GOODS_KEY, afterOrder);
                String msg = orderMessage(afterOrder);
                log.info(msg);
                return Result.ok(msg);
            } else {
                log.info(CLOSE);
                return Result.ok(CLOSE);
            }
        } finally {
            String script = "if redis.call('get',KEYS[1]) == ARGV[1]" +
                    "then " +
                    "    return redis.call('del',KEYS[1]) " +
                    "else " +
                    "    return 0 " +
                    "end";


            DefaultRedisScript getRedisScript = new DefaultRedisScript(script, Long.class);
            List<Serializable> keys = Lists.newArrayList(KEY_PREFIX_LOCK);
            Object[] objects = {value};
            Object o = redisUtil.execute(getRedisScript, keys, objects);

            if ("1".equals(o.toString())) {
                log.debug("unlock success ");
            } else {
                log.debug("unlock error in: {}", redisUtil.getCacheLock(KEY_PREFIX_LOCK));
            }
        }
    }

    @Override
    public Result seckillLv5() {
        RLock redisLock = redisson.getLock(KEY_PREFIX_LOCK);
        redisLock.lock();
        try {
            Integer num = getGoodsNum();
            if (num > 0) {
                Integer afterOrder = num - 1;
                redisUtil.cacheValue(RedisConstant.GOODS_KEY, afterOrder);
                String msg = orderMessage(afterOrder);
                log.info(msg);
                return Result.ok(msg);
            } else {
                log.info(CLOSE);
                return Result.ok(CLOSE);
            }
        } finally {
            redisLock.unlock();
        }
    }


    /**
     * 查询商品数量
     *
     * @return
     */
    private Integer getGoodsNum() {
        String num = redisUtil.getValue(RedisConstant.GOODS_KEY).toString();
        return StrUtil.isBlank(num) ? 0 : Integer.parseInt(num);
    }

    /**
     * 构建购买成功后提示信息
     *
     * @param num
     * @return
     */
    private String orderMessage(Integer num) {
        StringBuilder stringBuffer = new StringBuilder(ORDER);
        stringBuffer.append(" 商品还剩").append(num).append("件 ,端口 【").append(SystemConstant.PORT).append("】提供服务");
        return stringBuffer.toString();
    }
}
